﻿// //  ********************************************************************************
// //  Copyright (C) 2016 Sebastian Kenter. All rights reserved.
// // 
// //  Name    : PhotonServerProject | PhotonServerFramework | ClientPeerFactory.cs
// //  Created : 2016 - 03 - 21 11:36
// //  Modified: 2016 - 04 - 18 09:37
// //  ********************************************************************************

using System;
using ExitGames.Logging;
using NHibernate;
using Photon.SocketServer;
using PhotonServerCommon.Entities.Domain;
using PhotonServerFramework.Implementation.Database;
using PhotonServerFramework.Implementation.Server;
using PhotonServerFramework.Interfaces.Application;
using PhotonServerFramework.Interfaces.Client;
using PhotonServerFramework.Interfaces.Configuration;
using PhotonServerFramework.Interfaces.Handler;
using PhotonServerFramework.Interfaces.Server;

namespace PhotonServerFramework.Implementation.Client
{
    public class ClientPeerFactory : IClientPeerFactory
    {
        public ILogger Logger { get; }

        private readonly IClientHandlerRegistry _clientHandlerRegistry;
        private readonly IClientPeerRegistry _clientPeerRegistry;
        private readonly IServerApplication _server;
        private readonly IServerHandlerRegistry _serverHandlerRegistry;
        private readonly IServerPeerRegistry _serverPeerRegistry;

        public ClientPeerFactory(ILogger logger, IServerApplication server, IClientPeerRegistry clientPeerRegistry,
                                 IServerPeerRegistry serverPeerRegistry, IServerHandlerRegistry serverHandlerRegistry,
                                 IClientHandlerRegistry clientHandlerRegistry)
        {
            Logger = logger;
            _clientHandlerRegistry = clientHandlerRegistry;
            _server = server;
            _clientPeerRegistry = clientPeerRegistry;
            _serverPeerRegistry = serverPeerRegistry;
            _serverHandlerRegistry = serverHandlerRegistry;
        }

        public T CreateClientPeer<T>(IPeerConfiguration configuration) where T : class
        {
            InitRequest request = configuration.GetConfiguration<InitRequest>();

            if (request != null)
            {
                if (request.LocalPort == _server.Configuration.MasterTcpPort)
                {
                    Logger.InfoFormat("Received init request on port {0} - trying to create a new sub server peer",
                        request.LocalPort);
                    Logger.InfoFormat(
                        "About to create a new sub server peer (local: {0}:{1}, remote: {2}:{3}, connection: {4}, protocol: {5}, type= {6})",
                        request.LocalIP, request.LocalPort, request.RemoteIP, request.RemotePort, request.ConnectionId,
                        request.Protocol.ProtocolType, request.PhotonPeer.GetPeerType().ToString());

                    ServerPeer serverPeer = new ServerPeer(request.Protocol, request.PhotonPeer, Logger,
                        _serverHandlerRegistry, _server);

                    using (ISession session = SessionManager.OpenSession())
                    {
                        using (ITransaction transaction = session.BeginTransaction())
                        {
                            try
                            {
                                ServerPeerT peer = new ServerPeerT
                                                   {
                                                       Application = _server.Configuration.Name,
                                                       ConnectionId = request.ConnectionId,
                                                       Connected = 1,
                                                       LocalIp = request.LocalIP,
                                                       LocalPeerId = serverPeer.ServerPeerId.ToString(),
                                                       LocalPort = request.LocalPort,
                                                       LocalServerId = _server.ServerId.ToString(),
                                                       LocalServerType = (int) _server.ServerType,
                                                       Protocol = request.Protocol.ProtocolType.ToString(),
                                                       RemoteIp = request.RemoteIP,
                                                       RemotePeerId = null,
                                                       RemotePort = request.RemotePort,
                                                       RemoteServerId = null,
                                                       RemoteServerType = null,
                                                       DzIns = DateTime.Now,
                                                       DzUpd = DateTime.Now,
                                                       NamUpd = GetType().Name,
                                                       Tsn = 1
                                                   };

                                session.Save(peer);
                                transaction.Commit();
                            }
                            catch (Exception ex)
                            {
                                Logger.ErrorFormat("Operation failed with: {0}", ex.Message);
                                transaction.Rollback();
                            }
                        }
                    }

                    _serverPeerRegistry.RegisterServerPeer(serverPeer);

                    serverPeer.ConnectPeer();

                    return serverPeer as T;
                }

                if (request.LocalPort == _server.Configuration.ClientUdpPort)
                {
                    Logger.InfoFormat("Received init request on port {0} - trying to create a new client peer",
                        request.LocalPort);

                    Logger.InfoFormat(
                        "About to create a new client peer (local: {0}:{1}, remote: {2}:{3}, connection: {4}, protocol: {5}, type= {6})",
                        request.LocalIP, request.LocalPort, request.RemoteIP, request.RemotePort,
                        request.ConnectionId, request.Protocol.ProtocolType, request.PhotonPeer.GetPeerType().ToString());

                    ClientPeer clientPeer = new ClientPeer(request, Logger, _clientHandlerRegistry);

                    using (ISession session = SessionManager.OpenSession())
                    {
                        using (ITransaction transaction = session.BeginTransaction())
                        {
                            try
                            {
                                ClientPeerT peer = new ClientPeerT
                                                   {
                                                       LocalIp = request.LocalIP,
                                                       LocalPort = request.LocalPort,
                                                       ConnectedServerId = _server.ServerId.ToString(),
                                                       ConnectionId = request.ConnectionId,
                                                       Connected = 1,
                                                       PeerId = clientPeer.ClientPeerId.ToString(),
                                                       PeerType = request.PhotonPeer.GetPeerType().ToString(),
                                                       Protocol = request.Protocol.ProtocolType.ToString(),
                                                       RemoteIp = request.RemoteIP,
                                                       RemotePort = request.RemotePort,
                                                       Version = request.ClientVersion.ToString(),
                                                       DzIns = DateTime.Now,
                                                       DzUpd = DateTime.Now,
                                                       NamUpd = GetType().Name,
                                                       Tsn = 1
                                                   };

                                session.Save(peer);
                                transaction.Commit();
                            }
                            catch (Exception ex)
                            {
                                Logger.ErrorFormat("Operation failed with: {0}", ex.Message);
                                transaction.Rollback();
                            }
                        }
                    }

                    _clientPeerRegistry.RegisterClientPeer(clientPeer);

                    return clientPeer as T;
                }

                Logger.ErrorFormat("Initial request invalid - connection try to port {0}", request.LocalPort);
                return null;
            }

            Logger.ErrorFormat("Initial request invalid");
            return null;
        }
    }
}
